#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "include/muduo/net/TcpServer.h"
#include "include/muduo/net/EventLoop.h"
#include "include/muduo/net/TcpConnection.h"
#include "include/dispatcher.h"
#include "include/codec.h"

#include "request.pb.h"

#include <iostream>
#include <functional>
#include "memory"

class Server
{
public:
    // Server端收到的肯定是请求报文，所以我们需要将我们定义好的序列化字段的类型定义一个指针，因为muduo库中使用的都是智能指针
    typedef std::shared_ptr<request::RequestTranslate> RequestTranslatePtr;
    typedef std::shared_ptr<request::RequestAdd> RequestAddPtr;

    Server(int port)
        : _dispatcher(std::bind(&Server::defaultMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), _server(&_loop, muduo::net::InetAddress(port), "Server", muduo::net::TcpServer::kReusePort)
    {
        _dispatcher.registerMessageCallback<request::RequestTranslate>(std::bind(&Server::translate, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<request::RequestAdd>(std::bind(&Server::add, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        _server.setConnectionCallback(std::bind(&Server::onConnection, this, std::placeholders::_1));
        _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    void start()
    {
        _server.start();
        _loop.loop();
    }

    ~Server() {}

private:
    void onConnection(const muduo::net::TcpConnectionPtr &conn)
    {
        LOG_INFO << conn->peerAddress().toIpPort() << " -> "
                 << conn->localAddress().toIpPort() << " is "
                 << (conn->connected() ? "UP" : "DOWN");
    }

    void defaultMessageCallback(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp)
    {
        LOG_INFO << "onUnknownMessage: " << message->GetTypeName();
        conn->shutdown();
    }

    void Translate(std::string &str)
    {
        static const std::unordered_map<std::string, std::string> dict = {
            {"hello", "你好"},
            {"你好", "hello"},
            {"苹果", "apple"},
            {"apple", "苹果"}};

        if (dict.find(str) != dict.end())
        {
            str = dict.at(str);
        }
        else
        {
            str = "我不知道！";
        }
    }

    // 翻译处理函数收到的肯定是翻译请求
    void translate(const muduo::net::TcpConnectionPtr &conn, const RequestTranslatePtr &message, muduo::Timestamp)
    {
        // 接收请求字段
        std::string msg = message->msg();
        // 根据请求字段进行处理
        Translate(msg);
        // 定义相应字段变量
        request::ResponseTranslate resp;
        // 将结果设置到相应结构变量中
        resp.set_msg(msg);
        // 由_codec帮忙发送出去
        _codec.send(conn, resp);
    }

    // 加法处理函数收到的肯定是翻译请求
    void add(const muduo::net::TcpConnectionPtr &conn, const RequestAddPtr &message, muduo::Timestamp)
    {
        // 接收请求字段
        int num1 = message->num1();
        int num2 = message->num2();
        // 根据请求字段进行处理
        int result = num1 + num2;
        // 定义相应字段变量
        request::ResponseAdd resp;
        // 将结果设置到相应结构变量中
        resp.set_result(result);
        // 由_codec帮忙发送出去
        _codec.send(conn, resp);
    }

private:
    // 解析到其中的信息之后，我们需要通过_dispatcher来通过不同的请求类型调用不同的处理函数(这是我们要做的)，最后响应回客户端
    ProtobufDispatcher _dispatcher;

    // 服务启动之后收到的消息都会是基于protobuf结构化字段生成的序列，所以需要使用_codec来解析其中的信息
    ProtobufCodec _codec;

    // 基本的服务器tcp和事件监听loop
    muduo::net::EventLoop _loop;
    muduo::net::TcpServer _server;
};

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "error, " << argv[0] << " port" << std::endl;
        return 1;
    }

    int port = std::stoi(argv[1]);
    Server svr(port);
    svr.start();

    
    return 0;
}